<cfcomponent name="HibernateFactory" hint="Factory for getting to Hibernate objects">

<cfscript>
	instance = StructNew();
</cfscript>

<cffunction name="dumpvar">
  <cfargument name="var" default="blank">
  <cfargument name="abort" default="true">
  <cftry>
	  <cfdump var="#var#">
	<cfcatch>
	  <cfset writeoutput(cfcatch.Message & " " & cfcatch.Detail & " " & cfcatch.TagContext[1].line & " " & cfcatch.stacktrace) />
	</cfcatch>
	</cftry>
	<cfif arguments.abort>
	  <cfabort />
	</cfif>
</cffunction>

<cffunction name="newJavaObject" output="false">
  <cfargument name="class">
	<cfreturn createObject("java", arguments.class) />
</cffunction>

<!------------------------------------------- PUBLIC ------------------------------------------->

<cffunction name="init" hint="Constructor: Loads the hibernate.cfg.xml from here, and any *.hbm.xml files that are in this directory" access="public" returntype="HibernateService" output="false">
	<cfargument name="configuration" hint="config" required="Yes">
	<cfscript>
		var config = configuration;
		var hibConfig = newJavaObject("org.hibernate.cfg.Configuration");
		configureByStruct(config,hibConfig);
		hibConfig.addDirectory(newJavaObject("java.io.File").init(expandpath("./hbm")));
	 	instance.hibernateConfiguration = hibConfig;
		instance.sessionFactory = hibConfig.buildSessionFactory();
		instance.tablesMetaData = getConfigurationTablesMetaData();
		instance.allClassMetaData = getSessionFactory().getAllClassMetadata();
		instance.entityList = structKeyList(instance.allClassMetaData);
		instance.entitiesMetaData = getAllEntityMetaData(instance.allClassMetaData);
		instance.entityGenerator = createObject("component","EntityGenerator").init(getDirectoryFromPath(getCurrentTemplatePath()),instance.entitiesMetaData);		
		instance.entityAdapter = createObject("component","EntityAdapter").init(instance.entitiesMetaData);
		return this;
	</cfscript>
</cffunction>

<cffunction name="getSessionFactory" output="false">
	<cfreturn instance.sessionFactory />
</cffunction>

<cffunction name="getConfiguration" output="false">
	<cfreturn instance.hibernateConfiguration />
</cffunction>

<cffunction name="generateEntities" output="false">
	<cfreturn instance.entityGenerator.generate() />
</cffunction>


<cffunction name="query" output="false">
	<cfargument name="querystr" required="true" />
	<cfscript>
		var currentSession = "";
		var result = "";
			try {
		 		currentSession = instance.sessionFactory.openSession();			
				currentSession.beginTransaction();
				result = currentSession.createQuery(arguments.querystr).list();
				currentSession.getTransaction().commit();
			} 
			catch(Any excpt) {
				// dump of struct is endless loop do to cf introspection haywireness so just do msg/stack
			  dumpvar(excpt.message & " " & excpt.stacktrace);
			}
			return result;
	</cfscript>
</cffunction>

<cffunction name="getTablesMetaData" output="false">
	<cfreturn instance.tablesMetaData />
</cffunction>

<cffunction name="getConfigurationTablesMetaData" output="false">
	<cfset var dataStruct = structNew() />
	<cfset var tableIterator = getConfiguration().getTableMappings() />
	<cfset var table = "" />
	<cfset var tableName = "" />
	<cfset var column = "" />
	<cfset var columnMetaData = "" />
	<cfset var columnprops = "" />
	<cfset var propname = "" />
	<cfset dataStruct["tables"] = structNew() />
	<cfloop condition="#tableIterator.hasNext()#">
		<cfset table = tableIterator.next() />
		<cfscript>
			tableName = table.catalog & "." & table.name;
			dataStruct["tables"][tableName] = structNew();
			primaryKey = table.getPrimaryKey().getName();
			columnIterator = table.getColumnIterator();
			while(columnIterator.hasNext()) {
				column = columnIterator.next();
				columnMetaData = structNew();
				columnprops = structKeyList(column);
				for(x = 1; x lte listLen(columnprops);x = x+1) {
					try{
					propname = listGetAt(columnprops,x);
					if(propname eq "Value") {
						columnMetaData[propname] = column[propname];
					} else {
						columnMetaData[propname] = column[propname];
					}
					} catch (any expt) {}
					dataStruct["tables"][tableName]["columns"][column.getName()] = columnMetaData;
				}
			}
		</cfscript>
	</cfloop>
	<cfreturn dataStruct />
</cffunction>

<cffunction name="propdumper" output="false">
	<cfargument name="dumpthis">
	<cfset var dump = "" />
	<cfsavecontent variable="dump">
		<cfloop list="#structKeyList(dumpthis)#" index="pr">
			<cftry>
				<h3><cfoutput>#pr#</cfoutput></h3>
		  	<cfdump var="#dumpthis[pr]#">
		  	<hr />
				<cfcatch>No dump</cfcatch>
			</cftry>
		</cfloop>
	</cfsavecontent>
	<cfreturn dump />
</cffunction>

<cffunction name="getEntityList" output="false">
	<cfreturn instance.entityList />
</cffunction>

<cffunction name="load" output="false">
	<cfargument name="entityName" required="true" />
	<cfargument name="entityId" default="0" />
	<cfset var ent = structNew() />
	<cfset var entity = structNew() />
	<cftry>
		<cfset ent["record"] = getSessionFactory().openSession().get(entityName,javacast("int",entityId)) />
		<cfset entity = ent["record"] />
		<cfreturn entity />
	<cfcatch>
	  <cfreturn createObject("component",entityName).init() />
	</cfcatch>
	</cftry>
</cffunction>


<cffunction name="loadComponent" output="false">
	<cfargument name="entityName" required="true" />
	<cfargument name="entityId" default="" />
	<cfargument name="component" default="" />
	<cfset var ent = structNew() />
	<cfset var entity = structNew() />
	<cfif entityId eq "">
	  <cfset arguments.component = createObject("component",entityName).init()>
		<cfreturn component />
	</cfif>
	<cfset ent["record"] = getSessionFactory().openSession().get(entityName,javacast("int",entityId)) />
	<cfif NOT structKeyExists(ent,"record")>
	  <cfthrow type="No matching record found for: #entityName#:#entityID#">
	</cfif>
	<cfset entity = ent["record"] />
	<cfif NOT isStruct(arguments.component)>
	  <cfset arguments.component = createObject("component",entityName).init()>
	</cfif>
	<cfset instance.entityAdapter.setObjectTransferObject(arguments.component,structKeyList(entity),entity) />
	<cfreturn component />
</cffunction>


<cffunction name="rawsave" output="false">
	<cfargument name="entityName" required="true" />
	<cfargument name="hashmap" required="true" />
	<cfset var ent = structNew() />
	<cfset var entity = structNew() />
<!---
	<cfset dumpvar(getEntityMetaData(entityName))>
 	<cfset dumpvar(arguments)>
 --->

	<cfset arr = createObject("java", "java.util.HashMap").init(arguments.hashmap) />
		<cfset ent["record"] = getSessionFactory().openSession().saveOrUpdate(entityName,arguments.hashmap) />
	<cftry>
		<cfif NOT structKeyExists(ent,"record")>
		  <cfreturn>
		<cfelse>
			<cfdump var="#ent#"><cfabort />
		</cfif>
	<cfcatch type="any">
			arrr<cfabort />
	</cfcatch>
	</cftry>
</cffunction>

<cffunction name="save" output="false">
	<cfargument name="entityName" required="true" />
	<cfargument name="object" required="true" />
	<cfset var ent = structNew() />
	<cfset var entity = structNew() />
	<cfset var adaptedEntity = instance.entityAdapter.convertToHashmap(arguments.entityName,arguments.object) />
<!---
	<cfset dumpvar(adaptedEntity) />
	<cfset dumpvar(getEntityMetaData(entityName))>
 	<cfset dumpvar(arguments)>

	<cfset arr = createObject("java", "java.util.HashMap").init(arguments.hashmap) />
 --->
  <cftry>
		<cfset ent["record"] = getSessionFactory().openSession().save(adaptedEntity) />
		<cfset getSessionFactory().openSession().refresh(adaptedEntity)>
		<cfset instance.entityAdapter.setObjectTransferObject(arguments.object,structKeyList(adaptedEntity),adaptedEntity) />
	<cfcatch>
		<cfdump var="hibernate.save -  '#entityName#' error: #cfcatch.message#"><cfabort>
	</cfcatch>
	</cftry>
	<cfreturn arguments.object />
</cffunction>


<cffunction name="getAllEntityMetaData" output="false">
	<cfset var allEntities = structNew() />
	<cfset var entity = structNew() />
	<cfloop list="#getEntityList()#" index="ent">
		<cfset allEntities[ent] = getEntityMetaData(ent) />
	</cfloop>
	<cfreturn allEntities />
</cffunction>


<cffunction name="getEntityMetaData" output="false">
	<cfargument name="entityName" required="true">
	<cfscript>
	var entity = structNew();
	var entityMetaData = getSessionFactory().getClassMetadata(arguments.entityName);
	entity["entityName"] = arguments.entityName;
	entity["tablename"] = entityMetaData.getTableName();
	entity["properties"] = getEntityProperties(entityMetaData);
	entity["RootTableIdentifierColumnNames"] = entityMetaData["RootTableIdentifierColumnNames"];
	entity["ContraintOrderedTableKeyColumnClosure"] = entityMetaData["ContraintOrderedTableKeyColumnClosure"];
	entity["ConstraintOrderedTableNameClosure"] = entityMetaData["ConstraintOrderedTableNameClosure"];
	entity["RootTableKeyColumnNames"] = entityMetaData["RootTableKeyColumnNames"];
	entity["RootTableName"] = entityMetaData["RootTableName"];
	entity["KeyColumnNames"] = entityMetaData["KeyColumnNames"];
	entity["IdentifierPropertyName"] = entityMetaData["IdentifierPropertyName"];
  
  //EntityMode = createObject("java", "org.hibernate.EntityMode");
	//dumpvar(entityMetaData.instantiate(0,EntityMode.MAP));
	//dumpvar(structKeyList(entityMetaData),false);
	//writeOutput(propdumper(entityMetaData));
	//dumpvar("");
	//dumpvar(entity);
	return entity;
	</cfscript>
<!---
 --->
</cffunction>

<cffunction name="getEntityProperties" output="false">
	<cfargument name="entityMetaData" required="true">
	<cfscript>
	var tables = getTablesMetaData();
	var tableName = entityMetaData.getTableName();
	var columnName = "";
	var propertyName = "";
	var properties = structNew();
	var propertyNames = arrayToList(entityMetaData.getPropertyNames());
	var propertyTypes = entityMetaData.getPropertyTypes();
	if(NOT listFindNoCase(propertyNames,entityMetaData["IdentifierPropertyName"])) {
		propertyName = entityMetaData["IdentifierPropertyName"];
		columnName = entityMetaData.getPropertyColumnNames(propertyName);
		properties[propertyName]["propertyname"] = propertyName;
		properties[propertyName]["type"] = entityMetaData.getIdentifierType();
		properties[propertyName]["column"] = tables["tables"][tableName]["columns"][columnName[1]];
		properties[propertyName]["hibernateClass"] = properties[propertyName]["column"]["Value"].getClass().getName();
	}
	for(x=1; x lte listLen(propertyNames); x = x+1) {
		propertyName = listGetAt(propertyNames,x);
		columnName = entityMetaData.getPropertyColumnNames(propertyName);
		properties[propertyName]["propertyname"] = propertyName;
		properties[propertyName]["type"] = propertyTypes[x];
		properties[propertyName]["column"] = tables["tables"][tableName]["columns"][columnName[1]];
		properties[propertyName]["hibernateClass"] = properties[propertyName]["column"]["Value"].getClass().getName();
	}
	//dumpvar(properties);
	return properties;
	</cfscript>
<!---
 --->
</cffunction>

<cffunction name="getTableMetaData2" output="false">
	<cfset var dataStruct = structNew() />
	<cfset var tableIterator = getConfiguration().getClassMappings() />
	<cfset var table = "" />
	<cfset dataStruct["tables"] = structNew() />
	<cfloop condition="#tableIterator.hasNext()#">
		<cfset table = tableIterator.next() />
		<cfscript>
			table = table.getTable();
			dataStruct["tables"][table.getName()] = structNew();
			primaryKey = table.getPrimaryKey().getName();
			columnIterator = table.getColumnIterator();
			while(columnIterator.hasNext()) {
				column = columnIterator.next();
				columnMetaData = structNew();
				columnprops = structKeyList(column);
				for(x = 1; x lte listLen(columnprops);x = x+1) {
					try{
					columnMetaData[listGetAt(columnprops,x)] = column[listGetAt(columnprops,x)];
					} catch (any expt) {}
				dataStruct["tables"][table.getName()]["columns"][column.getName()] = columnMetaData;
				}
			}
		</cfscript>
		<cfdump var="#dataStruct#">
		<cfdump var="#getSessionFactory().getDialect()#">
		<cfdump var="#column.getAlias(getSessionFactory().getDialect())#" /><cfabort />
	</cfloop>
</cffunction>

<!------------------------------------------- PRIVATE ------------------------------------------->

<cffunction name="configureByStruct" hint="Runs the Hibernate configure method" access="public" returntype="any" output="false">
	<cfargument name="configparams" required="true" type="struct">
	<cfargument name="configuration">
	<cfscript>
		var hibConfig = "";
		var params = arguments.configparams;
		var paramskeys = structKeyList(params);
		try {
			hibConfig = arguments.configuration;
			for (p = 1; p lte listLen(paramskeys); p = p+1) {
				hibConfig.setProperty(listGetAt(paramskeys,p), params[listGetAt(paramskeys,p)]);
			}
		} 
		catch(Any excpt) {
			// dump of struct is endless loop do to cf introspection haywireness so just do msg/stack
		  dumpvar(excpt.message & " " & excpt.stacktrace);
		}
		return hibConfig;
	</cfscript>
</cffunction>

</cfcomponent>